package com.example.redisdemo.controller;

import com.example.redisdemo.entity.GoodsEntity;
import com.example.redisdemo.util.BeanCopyUtils;
import com.example.redisdemo.util.RedisUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 测试控制类
 *
 * @author hexinglin
 * @created 2021-08-06 9:24
 **/
@RestController
@RequestMapping("/redis")
public class DemoController {
    @Autowired
    private RedisTemplate<String,  String> redisTemplate;
    @Autowired
    private RedisTemplate<String,  Object> stringTemplate;
    @Autowired(required = false)
    private RedissonClient redissonClient;
    @Autowired
    private RedisUtils redisUtils;
/*
    @Autowired
    private RedisTemplate<String,Object> redTemplate;
*/
    //@Test
    @RequestMapping("lset/{key}/{num}")
    public void rabbit(@PathVariable("key") String key,@PathVariable("num") int num) {
        //stringTemplate.delete(key);
        GoodsEntity goodsEntity = new GoodsEntity();
        goodsEntity.setGoodCode("14545"+num);
        goodsEntity.setGoodName("测试"+num);
        stringTemplate.opsForList().rightPush(key,goodsEntity);
        stringTemplate.expire(key,60,TimeUnit.SECONDS);
    }

    @RequestMapping("lget/{key}")
    public List<GoodsEntity> lget(@PathVariable("key") String key) {
        List<Object> range = this.stringTemplate.opsForList().range(key, 0, -1);
        List<GoodsEntity> goodsEntity = BeanCopyUtils.copy(range, GoodsEntity.class);
        for (GoodsEntity entity : goodsEntity) {
            System.out.println(entity);
        }
        return goodsEntity;
    }
    @RequestMapping("lpop/{key}")
    public GoodsEntity lpop(@PathVariable("key") String key) {
        Object o = this.stringTemplate.opsForList().leftPop(key);
        GoodsEntity goodsEntity = new GoodsEntity();
        BeanUtils.copyProperties(o,goodsEntity);

        return goodsEntity;
    }
    /**
     *@Description: 设置数据
     *@Param:
     *@return:
     */
    @RequestMapping("hset/{key}/{num}")
    public void hset(@PathVariable("key") String key,@PathVariable("num") int num){
        ArrayList<GoodsEntity> list=new ArrayList<GoodsEntity>();
        GoodsEntity goodsEntity=null;
        for (int i = num; i <num+3 ; i++) {
             goodsEntity = new GoodsEntity("id"+i,"code"+i,"name"+i,System.currentTimeMillis());
            list.add(goodsEntity);
        }

        redisUtils.hSetList(key,key+num,list);
    }

    /**
    *@Description: 获取数据
    *@Param:
    *@return:
    */
    @RequestMapping(value = "get/{param}",produces ="text/html;charset=utf-8")
    public String get(@PathVariable("param") String param){
        return redisTemplate.opsForValue().get(param);
    }
    /**
     *@Description: 设置数据
     *@Param:
     *@return:
     */
    @RequestMapping("set/{key}/{value}")
    public void get(@PathVariable("key") String key,@PathVariable("value") String value){
         redisTemplate.opsForValue().set(key,value);
    }
    /**
    *@Description:
    *@Param: 递增1
    *@return:
    */
    @RequestMapping("incr/{seller}")
    public String incr(@PathVariable("seller") String seller){
        Long result=redisTemplate.opsForValue().increment(seller);
        System.out.println("递增后结果："+result);
        return result.toString();
    }
    /**
    *@Description: 卖货防止分布式情况超卖,单机情况
    *@Param:
    *@return:
    */
    @RequestMapping("seller1/{seller}")
    public String seller1(@PathVariable("seller") String seller){
        synchronized (this){
            Long num= Long.valueOf(redisTemplate.opsForValue().get(seller));
            if (num < 1) {
                System.out.println("已售罄");
                return "已售罄";
            }
            num--;
            System.out.println("商品还剩："+num);
            redisTemplate.opsForValue().set(seller,num.toString());
            return num.toString();
        }

    }
    /**
     *@Description: 卖货防止分布式情况超卖，集群模式
     *@Description: 利用redis的decr特性
     *@return: 此方法会超卖
     */
    @RequestMapping("seller2/{seller}")
    public String seller2(@PathVariable("seller") String seller){
        Long nums= Long.valueOf(redisTemplate.opsForValue().get(seller));
        Long num;
        if (nums >0){
             if (nums == 10) {
                try {
                    //模拟业务卡顿（此时会超卖）
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            num = redisTemplate.opsForValue().decrement(seller);
             if (num+1!=nums){
                 redisTemplate.opsForValue().increment(seller);
                 System.out.println("系统繁忙");
                 return "系统繁忙";
             }
            System.out.println("商品还剩："+num);
        } else{
            System.out.println("已售罄");
            return "已售罄";
        }
        return num.toString();
    }
    /**
     *@Description: 卖货防止分布式情况超卖，集群模式
     *@Description: 利用setnx的锁
     *@return:
     */
    @RequestMapping("seller3/{seller}")
    public String seller3(@PathVariable("seller") String seller){
        String lockKey = "lockKey";
        String clientId = UUID.randomUUID().toString();//加一个本线程的客户ID
        Long num;
        try {
            boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId,30,TimeUnit.SECONDS); //相当于jedis.setnx(key,value)
            if (!flag) {
                System.out.println("系统繁忙，请重试！");
                return "系统繁忙，请重试！";
            }
            num = Long.valueOf(redisTemplate.opsForValue().get(seller));
            if (num >0){
              /*  num--;
                System.out.println("商品还剩："+num);
                redisTemplate.opsForValue().set(seller,num.toString());*/
                num = redisTemplate.opsForValue().decrement(seller);
                System.out.println("商品还剩："+num);
            }else {
                System.out.println("已售罄");
                return "已售罄";
            }
        } finally {
            if(clientId.equals(redisTemplate.opsForValue().get(lockKey))) {
                //跟本线程ID对比，确保本线程执行完后才删除相应锁
                redisTemplate.delete(lockKey);//业务结束，释放锁
            }


        }

        return num.toString();
    }
    /**
     *@Description: 卖货防止分布式情况超卖，集群模式
     *@Description: 利用setnx的锁
     *@return:
     */
    @RequestMapping("seller4/{seller}")
    public String seller4(@PathVariable("seller") String seller){
        RLock lock = redissonClient.getLock(seller+"101");
        lock=redissonClient.getRedLock(lock);
        Long num;
        try {
            lock.lock();
            num = Long.valueOf(redisTemplate.opsForValue().get(seller));
            if (num >0){
                num--;
                System.out.println("商品还剩："+num);
                redisTemplate.opsForValue().set(seller,num.toString());
               /* num = redisTemplate.opsForValue().decrement(seller);
                System.out.println("商品还剩："+num);*/
            }else {
                System.out.println("已售罄");
                return "已售罄";
            }
        } finally {
            lock.unlock();
        }

        return num.toString();
    }
}
